#ifndef MY_MATRIX_HPP_
#define MY_MATRIX_HPP_
#include "../../include/mrandom.hpp"
#include <cassert>
#include <cmath>
#include <iostream>
class Matrix {
  double *elem_;
  int row_, column_;
  // mutable bool own{true};

public:
  ~Matrix() {
    // if (own)
    delete[] elem_;
  }
  Matrix(int i, int j) : row_{i}, column_{j}, elem_{new double[i * j]} {}
  Matrix() = default;

  Matrix &operator=(const double value) noexcept {
    const int size = row_ * column_;
    for (int i = 0; i < size; ++i)
      elem_[i] = value;
    return *this;
  }
  double &operator()(int i, int j) noexcept {
    assert(i < row_ && j < column_);
    return elem_[i * column_ + j];
  }
  double &operator()(int i, int j) const noexcept {
    assert(i < row_ && j < column_);
    return elem_[i * column_ + j];
  }
  void RandomInitialize(const double begin = 0.0,
                        const double end = 100.0) noexcept {
    RandDouble generate_double(begin, end);
    const int size = row_ * column_;
    for (int i = 0; i < size; ++i)
      elem_[i] = generate_double();
  }
  bool IsSame(const Matrix &mat, const double tolerance = 1.0e-14) const
      noexcept {
    if ((row_ != mat.row_) || (column_ != mat.column_))
      return false;
    const int size = row_ * column_;
    for (int i = 0; i < size; ++i) {
      if (std::fabs(elem_[i] - mat.elem_[i]) > tolerance)
        return false;
    }
    return true;
  }
  double *Data() noexcept { return elem_; }
  int GetRow() const { return row_; }
  int GetColumn() const { return column_; }

  friend std::ostream &operator<<(std::ostream &os, const Matrix &mat) {

    for (int i = 0; i < mat.row_; ++i) {
      for (int j = 0; j < mat.column_; ++j)
        os << mat(i, j) << "  ";
      os << "\n";
    }
    return os;
  }
};


#endif